package com.xuecheng.orders.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xuecheng.base.execption.XueChengPlusException;
import com.xuecheng.base.utils.IdWorkerUtils;
import com.xuecheng.base.utils.QRCodeUtil;
import com.xuecheng.messagesdk.model.po.MqMessage;
import com.xuecheng.messagesdk.service.MqMessageService;
import com.xuecheng.orders.config.AlipayConfig;
import com.xuecheng.orders.config.PayNotifyConfig;
import com.xuecheng.orders.mapper.XcOrdersGoodsMapper;
import com.xuecheng.orders.mapper.XcOrdersMapper;
import com.xuecheng.orders.mapper.XcPayRecordMapper;
import com.xuecheng.orders.model.dto.AddOrderDto;
import com.xuecheng.orders.model.dto.PayRecordDto;
import com.xuecheng.orders.model.dto.PayStatusDto;
import com.xuecheng.orders.model.po.XcOrders;
import com.xuecheng.orders.model.po.XcOrdersGoods;
import com.xuecheng.orders.model.po.XcPayRecord;
import com.xuecheng.orders.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    XcOrdersMapper ordersMapper;

    @Autowired
    XcOrdersGoodsMapper ordersGoodsMapper;

    @Autowired
    XcPayRecordMapper payRecordMapper;

    @Autowired
    OrderService currentProxy;

    @Autowired
    MqMessageService mqMessageService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Value("${pay.qrcodeurl}")
    String qrcodeurl;

    @Value("${pay.alipay.APP_ID}")
    String APP_ID;

    @Value("${pay.alipay.APP_PRIVATE_KEY}")
    String APP_PRIVATE_KEY;

    @Value("${pay.alipay.ALIPAY_PUBLIC_KEY}")
    String ALIPAY_PUBLIC_KEY;


    /**
     * @description 创建商品订单
     * @return PayRecordDto 支付记录(包括二维码)
     * @param addOrderDto 订单信息
     * @author Mr.M
     * @date 2022/10/4 11:02
     */
    @Transactional
    @Override
    public PayRecordDto createOrder(String userId, AddOrderDto addOrderDto) {

        //1.添加商品订单，订单表、明细表
        XcOrders xcOrders = saveXcOrders(userId, addOrderDto);

        //2.添加支付记录
        XcPayRecord payRecord = createPayRecord(xcOrders);

        //3.生成二维码
        String qrCode = null;
        try {
            //url要可以被模拟器访问到，url为下单接口(稍后定义)
            String url = String.format(qrcodeurl, payRecord.getPayNo());
            qrCode = new QRCodeUtil().createQRCode(url, 200, 200);
        } catch (IOException e) {
            XueChengPlusException.cast("生成二维码出错");
        }
        PayRecordDto payRecordDto = new PayRecordDto();
        BeanUtils.copyProperties(payRecord,payRecordDto);
        payRecordDto.setQrcode(qrCode);

        return payRecordDto;
    }




    /**
     * @description 查询支付记录
     * @param payNo  交易记录号
     * @return com.xuecheng.orders.model.po.XcPayRecord
     * @author Mr.M
     * @date 2022/10/20 23:38
     */
    @Override
    public XcPayRecord getPayRecordByPayno(String payNo) {
        XcPayRecord xcPayRecord = payRecordMapper.selectOne(new LambdaQueryWrapper<XcPayRecord>().eq(XcPayRecord::getPayNo, payNo));
        return xcPayRecord;
    }



    //根据选课id查询订单表
    public XcOrders getOrderByBusinessId(String businessId) {

        //查询条件
        LambdaQueryWrapper<XcOrders> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(XcOrders::getOutBusinessId,businessId);//选课id

        //查询
        XcOrders xcOrders = ordersMapper.selectOne(lambdaQueryWrapper);

        return xcOrders;
    }


    //添加商品订单，订单表、明细表
    public XcOrders saveXcOrders(String userId,AddOrderDto addOrderDto){

        //1.添加订单表
        //幂等性判断，同一个选课记录只有一个订单
        XcOrders order = getOrderByBusinessId(addOrderDto.getOutBusinessId());
        //如果订单已存在，不添加
        if (order != null){
            return order;
        }

        //不存在就添加订单
        order = new XcOrders();
        //设置参数
        long id = IdWorkerUtils.getInstance().nextId();
        order.setId(id);//订单id
        order.setTotalPrice(addOrderDto.getTotalPrice());//总价
        order.setCreateDate(LocalDateTime.now());//创建时间
        order.setStatus("600001");//交易状态：未支付
        order.setUserId(userId);//用户id
        order.setOrderType(addOrderDto.getOrderType());//订单类型
        order.setOrderName(addOrderDto.getOrderName());//订单名称
        order.setOrderDetail(addOrderDto.getOrderDetail());//订单明细json
        order.setOrderDescrip(addOrderDto.getOrderDescrip());//订单描述
        order.setOutBusinessId(addOrderDto.getOutBusinessId());//选课记录id
        //插入
        int i = ordersMapper.insert(order);
        //如果插入失败提示
        if (i <= 0){
            XueChengPlusException.cast("插入订单表失败");
        }

        //2.添加订单明细表
        String orderDetailJson = addOrderDto.getOrderDetail();
        //转成XcOrdersGoods类
        List<XcOrdersGoods> xcOrdersGoods = JSON.parseArray(orderDetailJson, XcOrdersGoods.class);

        //循环添加进入明细表
        for (XcOrdersGoods goods : xcOrdersGoods) {
            XcOrdersGoods xcOrdersGoods1 = new XcOrdersGoods();
            xcOrdersGoods1.setOrderId(id);//订单号
            xcOrdersGoods1.setGoodsId(goods.getGoodsId());//商品id
            xcOrdersGoods1.setGoodsType(goods.getGoodsType());//商品类型
            xcOrdersGoods1.setGoodsName(goods.getGoodsName());//商品名称
            xcOrdersGoods1.setGoodsPrice(goods.getGoodsPrice());//商品价格
            xcOrdersGoods1.setGoodsDetail(goods.getGoodsDetail());//商品详情json
            ordersGoodsMapper.insert(xcOrdersGoods1);
        }

        return order;
    }



    //添加支付记录
    public XcPayRecord createPayRecord(XcOrders orders){

        //1.如果订单不存在不能添加到支付记录
        if (orders == null){
            XueChengPlusException.cast("订单不存在");
        }

        //2.如果订单状态为“已支付”,不再添加支付记录
        if (orders.getStatus().equals("600002")){
            XueChengPlusException.cast("订单已支付");
        }

        //3.添加到支付记录
        XcPayRecord payRecord = new XcPayRecord();
        //设置参数
        long payNo = IdWorkerUtils.getInstance().nextId();
        payRecord.setPayNo(payNo);//支付交易流水号
        payRecord.setOrderId(orders.getId());//商品订单号
        payRecord.setOrderName(orders.getOrderName());//订单名称
        payRecord.setTotalPrice(orders.getTotalPrice());//总价格
        payRecord.setCurrency("CNY");//币种
        payRecord.setCreateDate(LocalDateTime.now());//创建时间
        payRecord.setStatus("601001");//未支付
        payRecord.setUserId(orders.getUserId());//用户id
        //插入
        payRecordMapper.insert(payRecord);

        return payRecord;
    }



    /**
     * 请求支付宝查询支付结果
     * @param payNo 支付记录id
     * @return 支付记录信息
     */
    @Override
    public PayRecordDto queryPayResult(String payNo){
        XcPayRecord payRecord = getPayRecordByPayno(payNo);
        if (payRecord == null) {
            XueChengPlusException.cast("请重新点击支付获取二维码");
        }
        //支付状态
        String status = payRecord.getStatus();
        //如果支付成功直接返回
        if ("601002".equals(status)) {
            PayRecordDto payRecordDto = new PayRecordDto();
            BeanUtils.copyProperties(payRecord, payRecordDto);
            return payRecordDto;
        }
        //从支付宝查询支付结果
        PayStatusDto payStatusDto = queryPayResultFromAlipay(payNo);
        //保存支付结果
        currentProxy.saveAliPayStatus( payStatusDto);
        //重新查询支付记录
        payRecord = getPayRecordByPayno(payNo);
        PayRecordDto payRecordDto = new PayRecordDto();
        BeanUtils.copyProperties(payRecord, payRecordDto);
        return payRecordDto;

    }


    //请求支付宝查询支付结果
    public PayStatusDto queryPayResultFromAlipay(String payNo){

        //========请求支付宝查询支付结果=============
        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, APP_ID, APP_PRIVATE_KEY, "json", AlipayConfig.CHARSET, ALIPAY_PUBLIC_KEY, AlipayConfig.SIGNTYPE); //获得初始化的AlipayClient
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", payNo);
        request.setBizContent(bizContent.toString());
        AlipayTradeQueryResponse response = null;
        try {
            response = alipayClient.execute(request);
            if (!response.isSuccess()) {
                XueChengPlusException.cast("请求支付查询查询失败");
            }
        } catch (AlipayApiException e) {
            log.error("请求支付宝查询支付结果异常:{}", e.toString(), e);
            XueChengPlusException.cast("请求支付查询查询失败");
        }

        //获取支付结果
        String resultJson = response.getBody();
        //转map
        Map resultMap = JSON.parseObject(resultJson, Map.class);
        Map alipay_trade_query_response = (Map) resultMap.get("alipay_trade_query_response");
        //支付结果
        String trade_status = (String) alipay_trade_query_response.get("trade_status");
        String total_amount = (String) alipay_trade_query_response.get("total_amount");
        String trade_no = (String) alipay_trade_query_response.get("trade_no");
        //保存支付结果
        PayStatusDto payStatusDto = new PayStatusDto();
        payStatusDto.setOut_trade_no(payNo);
        payStatusDto.setTrade_status(trade_status);
        payStatusDto.setApp_id(APP_ID);
        payStatusDto.setTrade_no(trade_no);
        payStatusDto.setTotal_amount(total_amount);
        return payStatusDto;

    }



    /**
     * @description 保存支付宝支付结果
     * @param payStatusDto  支付结果信息 从支付宝查询到的信息
     * @return void
     * @author Mr.M
     * @date 2022/10/4 16:52
     */
    @Transactional
    public void saveAliPayStatus(PayStatusDto payStatusDto){
        //支付记录号
        String payNO = payStatusDto.getOut_trade_no();
        XcPayRecord payRecordByPayno = getPayRecordByPayno(payNO);
        if(payRecordByPayno == null){
            XueChengPlusException.cast("找不到相关的支付记录");
        }
        //拿到相关联的订单id
        Long orderId = payRecordByPayno.getOrderId();
        XcOrders xcOrders = ordersMapper.selectById(orderId);
        if(xcOrders == null){
            XueChengPlusException.cast("找不到相关联的订单");
        }
        //支付状态
        String statusFromDb = payRecordByPayno.getStatus();
        //如果数据库支付的状态已经是成功了，不再处理了
        if("601002".equals(statusFromDb)){
            return ;
        }

        //如果支付成功
        String trade_status = payStatusDto.getTrade_status();//从支付宝查询到的支付结果
        if(trade_status.equals("TRADE_SUCCESS")){//支付宝返回的信息为支付成功
            //更新支付记录表的状态为支付成功
            payRecordByPayno.setStatus("601002");
            //支付宝的订单号
            payRecordByPayno.setOutPayNo(payStatusDto.getTrade_no());
            //第三方支付渠道编号
            payRecordByPayno.setOutPayChannel("Alipay");
            //支付成功时间
            payRecordByPayno.setPaySuccessTime(LocalDateTime.now());
            payRecordMapper.updateById(payRecordByPayno);

            //更新订单表的状态为支付成功
            xcOrders.setStatus("600002");//订单状态为交易成功
            ordersMapper.updateById(xcOrders);

            //将消息写到数据库
            MqMessage mqMessage = mqMessageService.addMessage("payresult_notify", xcOrders.getOutBusinessId(), xcOrders.getOrderType(), null);
            //发送消息
            notifyPayResult(mqMessage);

        }
    }





    /**
     * 发送通知结果
     * @param message
     */
    @Override
    public void notifyPayResult(MqMessage message) {

        //消息内容
        String jsonString = JSON.toJSONString(message);
        //创建一个持久化消息
        Message messageObj = MessageBuilder.withBody(jsonString.getBytes(StandardCharsets.UTF_8)).setDeliveryMode(MessageDeliveryMode.PERSISTENT).build();

        //消息id
        Long id = message.getId();

        //全局消息id
        CorrelationData correlationData = new CorrelationData(id.toString());

        //使用correlationData指定回调方法
        correlationData.getFuture().addCallback(result->{
            if(result.isAck()){
                //消息成功发送到了交换机
                log.debug("发送消息成功:{}",jsonString);
                //将消息从数据库表mq_message删除
                mqMessageService.completed(id);

            }else{
                //消息发送失败
                log.debug("发送消息失败:{}",jsonString);
            }

        },ex->{
            //发生异常了
            log.debug("发送消息异常:{}",jsonString);
        });
        //发送消息
        rabbitTemplate.convertAndSend(PayNotifyConfig.PAYNOTIFY_EXCHANGE_FANOUT,"",messageObj,correlationData);

    }
}